﻿using System;

using SharedGenomics.Core;

namespace SharedGenomics.Workbench.Core
{
    /// <summary>
    /// Implementation of the Maybe monad - this is used for functions that 'may' return a value. A common usage will be service methods which will not return if no login is present and the 
    /// caller needs to be made aware of this possibility explicitly. An alternative would be to return null, but this doesn't work for value types and is not explicit in the method 
    /// signature.
    /// </summary>
    /// <typeparam name="T">The type of the value contained in this instance</typeparam>
    //NOTE: this could (should?) be moved to SharedGenomics.Core
    public class Maybe<T>
    {
        private T _value;

        public Maybe()
        {
        }

        public Maybe(T value)
        {
            this.Value = value;
        }

        public T Value
        {
            get
            {
                ExceptionHelper.ThrowIf<InvalidOperationException>(!this.HasValue, "This maybe has no value");
                return this._value;
            }
            private set
            {
                this._value = value;
                this.HasValue = true;
            }
        }

        public bool HasValue { get; private set; }
    }

    public static class MaybeExtensionMethods
    {
        public static void BindAction<T>(this Maybe<T> maybe, Action<T> returnAction)
        {
            maybe.BindAction(returnAction, () => { });
        }

        public static void BindAction<T>(this Maybe<T> maybe, Action<T> returnAction, Action failureAction)
        {
            maybe.ThrowIfNull("maybe");

            if (maybe.HasValue)
            {
                returnAction(maybe.Value);
            }
            else
            {
                failureAction();
            }
        }

        public static Maybe<TOut> Bind<TIn, TOut>(this Maybe<TIn> maybe, Func<TIn, TOut> transform)
        {
            maybe.ThrowIfNull("maybe");

            return maybe.HasValue
                ? transform(maybe.Value).Just()
                : new Maybe<TOut>();
        }

        public static Maybe<T> Just<T>(this T value)
        {
            return new Maybe<T>(value);
        }
    }
}
